/************************************ Copyright *********************************
** (C) Copyright 2022-2025 ZHB,China.
** All Rights Reserved.
******************************************************************************
**--------------------------------------------------------------------------**
** @FileName      : peripheral_w25qxx.c  
** @Brief         : W25Qxx系列存储Flash操作API实现
**--------------------------------------------------------------------------**
** @Author Data   : zhangb 2025-07-23
** @Version       : v1.0				
**--------------------------------------------------------------------------**
********************************************************************************/

#include "peripheral_w25qxx.h"
#include "stm32_bsp_spi.h"
#include "main.h"
#include "spi.h"
#include <stdio.h>

#define W25Qxx_SPI  hspi1

#define W25Qxx_Enable()              HAL_GPIO_WritePin(MM_nCS_GPIO_Port, MM_nCS_Pin, GPIO_PIN_RESET)
#define W25Qxx_Disable()             HAL_GPIO_WritePin(MM_nCS_GPIO_Port, MM_nCS_Pin, GPIO_PIN_SET)



/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: get_w25qxx_tick
** @brief		: 获取时间
** @param		: None 参数
** @retva		: None 返回值
** @note		: None 备注
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
static uint32_t get_w25qxx_tick(void)
{
    return HAL_GetTick();
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: is_w25qxx_timeout
** @brief		: 判断是否超声
** @param		: lasttime 上一次时间
** @param		: timeout  超时时间
** @retva		: None 返回值
** @note		: None 备注
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
static uint8_t is_w25qxx_timeout(uint32_t lasttime, uint32_t timeout)
{
    uint32_t newtime = HAL_GetTick();
    if ((newtime - lasttime) >= timeout)
    {
        return W25Qxx_TIMEOUT;
    }
    else
    {
        return W25Qxx_OK;
    }
}
/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: init_peripheral_w25qxx
** @brief		: norflash w25qxx初始化
** @param		: None 参数
** @retva		: None 返回值
** @note		: None 备注
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
void init_peripheral_w25qxx(void)
{
    W25Qxx_Disable();
}

/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: reset_w25qxx
** @brief		: 重启设备芯片
** @param		: None 
** @retva		: None 
** @note		: None 
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
void reset_w25qxx(void)
{
    uint8_t cmd[2] = {RESET_ENABLE_CMD, RESET_MEMORY_CMD};
    
    W25Qxx_Enable();
    HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 2, W25Qxx_TIMEOUT_VALUE);
    W25Qxx_Disable();
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: read_w25qxx_status
** @brief		: 读取并判断W25qxx忙状态
** @param		: None
** @retva		: W25Qxx_OK空闲 W25Qxx_BUSY忙
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
uint8_t read_w25qxx_status(void)
{
    uint8_t cmd[] = {READ_STATUS_REG1_CMD};
    uint8_t status;
    
    W25Qxx_Enable();
    HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 1, W25Qxx_TIMEOUT_VALUE);
    HAL_SPI_Receive(&W25Qxx_SPI, &status, 1, W25Qxx_TIMEOUT_VALUE);
    W25Qxx_Disable();
    
    if ((status & W25Qxx_FSR_BUSY) != 0)
    {
        return W25Qxx_BUSY;
    }
    else
    {
        return W25Qxx_OK;
    }
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: enable_w25qxx_write
** @brief		: 写使能
** @param		: None
** @retva		: None
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
uint8_t enable_w25qxx_write(void)
{
    uint8_t cmd[] = {WRITE_ENABLE_CMD};
    uint32_t tickstart = get_w25qxx_tick();
    
    W25Qxx_Enable();
    HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 1, W25Qxx_TIMEOUT_VALUE);
    W25Qxx_Disable();
    
    while (1)
    {
        if (read_w25qxx_status() == W25Qxx_OK)
        {
            break;
        }
        else if (is_w25qxx_timeout(tickstart, W25Qxx_TIMEOUT_VALUE) == W25Qxx_TIMEOUT) 
        {
            return W25Qxx_TIMEOUT;
        }
    }
    
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: read_peripheral_w25qxx_ID
** @brief		: 读取芯片ID
** @param		: None
** @retva		: 芯片类型ID值
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-23
** @version		: V1.0
***************************************************************************/ 
uint16_t read_peripheral_w25qxx_typeID(void)
{
    uint16_t chip_type = 0;
    uint8_t w25qxx_ID[4];
    uint8_t cmd[4] = {READ_ID_CMD,0x00,0x00,0x00};
    
    W25Qxx_Enable();
    HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 4, W25Qxx_TIMEOUT_VALUE);
    HAL_SPI_Receive(&W25Qxx_SPI, w25qxx_ID, 2, W25Qxx_TIMEOUT_VALUE);
    W25Qxx_Disable();
    
    chip_type = w25qxx_ID[0];
    chip_type = w25qxx_ID[1] + (chip_type<<8);
    
    return chip_type;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: erase_peripheral_w25qxx_block
** @brief		: 扇区擦除W25Qxx系列芯片Flash
** @param		: address   W25Qxx芯片存储地址所在的扇区
** @retva		: 成功：W25Qxx_OK  失败：W25Qxx_TIMEOUT
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
uint8_t erase_peripheral_w25qxx_sector(uint32_t address)
{
    uint8_t cmd[5];
    uint32_t tickstart = get_w25qxx_tick();    
    if (W25Qxx_TYPE == TYPE_W25Q256)
    {
        cmd[0] = SECTOR_ERASE_32BIT_ADDR_CMD;
        cmd[1] = (uint8_t)(address >> 24);
        cmd[2] = (uint8_t)(address >> 16);
        cmd[3] = (uint8_t)(address >> 8);
        cmd[4] = (uint8_t)(address);
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 5, W25Qxx_SECTOR_ERASE_MAX_TIME);
        W25Qxx_Disable();
    }
    else
    {
        cmd[0] = SECTOR_ERASE_CMD;
        cmd[1] = (uint8_t)(address >> 16);
        cmd[2] = (uint8_t)(address >> 8);
        cmd[3] = (uint8_t)(address);
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 4, W25Qxx_SECTOR_ERASE_MAX_TIME);
        W25Qxx_Disable();
    }
    
    while (1)
    {
        if (read_w25qxx_status() == W25Qxx_OK)
        {
            break;
        }
        else if (is_w25qxx_timeout(tickstart, W25Qxx_SECTOR_ERASE_MAX_TIME) == W25Qxx_TIMEOUT) 
        {
            return W25Qxx_TIMEOUT;
        }
    }
    
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: erase_peripheral_w25qxx_chip
** @brief		: 整片擦除W25QXX存储芯片
** @param		: None
** @retva		: 成功：W25Qxx_OK  失败：W25Qxx_TIMEOUT
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
uint8_t erase_peripheral_w25qxx_chip(void)
{
    uint8_t cmd[5];
    uint32_t tickstart = get_w25qxx_tick();
    
    cmd[0] = SECTOR_ERASE_CMD;
    
    W25Qxx_Enable();
    HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 1, W25Qxx_BULK_ERASE_MAX_TIME);
    W25Qxx_Disable();
    
    while (1)
    {
        if (read_w25qxx_status() == W25Qxx_OK)
        {
            break;
        }
        else if (is_w25qxx_timeout(tickstart, W25Qxx_BULK_ERASE_MAX_TIME) == W25Qxx_TIMEOUT) 
        {
            return W25Qxx_TIMEOUT;
        }
    }
    
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: read_peripheral_w25qxx_data
** @brief		: 读取W25Qxx存储ReadAddr地址开始的Size长度数据
** @param		: ReadAddr      存储地址
** @param		: pData         待读取数据的指针缓冲区
** @param		: Size          待读取数据的长度
** @retva		: 成功：W25Qxx_OK
** @note		: None 备注
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
uint8_t read_peripheral_w25qxx_data(uint32_t ReadAddr, uint8_t* pData, uint32_t Size)
{
    uint8_t cmd[5];
    if ((pData == NULL) || (Size == 0))
    {
        return W25Qxx_ERROR;
    }
    if (W25Qxx_TYPE == TYPE_W25Q256)
    {
        cmd[0] = READ_32BIT_ADDR_CMD;
        cmd[1] = (uint8_t)(ReadAddr >> 24);
        cmd[2] = (uint8_t)(ReadAddr >> 16);
        cmd[3] = (uint8_t)(ReadAddr >> 8);
        cmd[4] = (uint8_t)(ReadAddr);
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 5, W25Qxx_TIMEOUT_VALUE);
        HAL_SPI_Receive(&W25Qxx_SPI, pData, Size, W25Qxx_TIMEOUT_VALUE);
        W25Qxx_Disable();
    }
    else
    {
        cmd[0] = READ_CMD;
        cmd[1] = (uint8_t)(ReadAddr >> 16);
        cmd[2] = (uint8_t)(ReadAddr >> 8);
        cmd[3] = (uint8_t)(ReadAddr);
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 4, W25Qxx_TIMEOUT_VALUE);
        HAL_SPI_Receive(&W25Qxx_SPI, pData, Size, W25Qxx_TIMEOUT_VALUE);
        W25Qxx_Disable();
    }
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: page_program_peripheral_w25qxx
** @brief		: 页编程写入w25qxx
** @param		: writeAddr     待写入数据的存储地址
** @param		: pData         待写入数据的指针缓冲区
** @param		: Size          待写入数据的长度
** @retva		: 成功：W25Qxx_OK
** @note		: None 备注
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
uint8_t page_program_peripheral_w25qxx(uint32_t writeAddr, uint8_t* pData, uint32_t Size)
{
    uint8_t cmd[5];
    uint32_t tickstart = get_w25qxx_tick();
    
    if ((pData == NULL) || (Size == 0))
    {
        return W25Qxx_ERROR;
    }
        
    if (W25Qxx_TYPE == TYPE_W25Q256)
    {
        cmd[0] = PAGE_PROG_32BIT_ADDR_CMD;
        cmd[1] = (uint8_t)(writeAddr >> 24);
        cmd[2] = (uint8_t)(writeAddr >> 16);
        cmd[3] = (uint8_t)(writeAddr >> 8);
        cmd[4] = (uint8_t)(writeAddr);
        
        enable_w25qxx_write();
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 5, W25Qxx_TIMEOUT_VALUE);
        HAL_SPI_Transmit(&W25Qxx_SPI, pData, Size, W25Qxx_TIMEOUT_VALUE);
        W25Qxx_Disable();
    }
    else
    {
        cmd[0] = PAGE_PROG_CMD;
        cmd[1] = (uint8_t)(writeAddr >> 16);
        cmd[2] = (uint8_t)(writeAddr >> 8);
        cmd[3] = (uint8_t)(writeAddr);
        
        enable_w25qxx_write();
        
        W25Qxx_Enable();
        HAL_SPI_Transmit(&W25Qxx_SPI, cmd, 4, W25Qxx_TIMEOUT_VALUE);
        HAL_SPI_Transmit(&W25Qxx_SPI, pData, Size, W25Qxx_TIMEOUT_VALUE);
        W25Qxx_Disable();
    }
    
    while (1)
    {
        if (read_w25qxx_status() == W25Qxx_OK)
        {
            break;
        }
        else if (is_w25qxx_timeout(tickstart, W25Qxx_TIMEOUT_VALUE) == W25Qxx_TIMEOUT) 
        {
            return W25Qxx_TIMEOUT;
        }
    }
    
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: write_peripheral_w25qxx_data
** @brief		: 写入数据到W25QXX存储芯片
** @param		: writeAddr     待写入数据的存储地址
** @param		: pData         待写入数据的指针缓冲区
** @param		: Size          待写入数据的长度
** @retva		: 成功：W25Qxx_OK
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
uint8_t write_peripheral_w25qxx_data(uint32_t writeAddr, uint8_t* pData, uint32_t Size)
{
    uint32_t end_addr, current_size, current_addr;
    if ((pData == NULL) || (Size == 0))
    {
        return W25Qxx_ERROR;
    }
    
    current_addr = 0;

    while (current_addr <= writeAddr)
    {
        current_addr += W25Qxx_PAGE_SIZE;
    }
    current_size = current_addr - writeAddr;
    
    if (current_size > Size)
    {
        current_size = Size;
    }
    
    current_addr = writeAddr;
    end_addr = writeAddr + Size;
    
    do {
        
        if (page_program_peripheral_w25qxx(current_addr, pData,current_size) != W25Qxx_OK)
        {
            return W25Qxx_ERROR;
        }
        
        current_addr += current_size;
        pData += current_size;
        current_size = ((current_addr + W25Qxx_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Qxx_PAGE_SIZE;
        
    } while (current_addr < end_addr);
    
    return W25Qxx_OK;
}


/*************************************************************************** 
**------------------------------------------------------------------------**
** @name		: peripheral_w25qxx_test
** @brief		: 测试w25qxx
** @param		: None
** @retva		: None
** @note		: None
**------------------------------------------------------------------------**
** @author		: zhangbo	2025-07-24
** @version		: V1.0
***************************************************************************/ 
void peripheral_w25qxx_test(void)
{
    uint8_t wTestBuf[100];
    uint8_t rTestBuf[100];
    
    for (int i=0;i<100;i++)
    {
        wTestBuf[i] = i;
    }
    
    read_peripheral_w25qxx_data(W25Qxx_TEST_ADDR, rTestBuf, 100);
    for (int i=0;i<100;i++)
    {
        printf("%d ", rTestBuf[i]);
    }
    printf("\r\n");
    erase_peripheral_w25qxx_sector(W25Qxx_TEST_ADDR);
    
    write_peripheral_w25qxx_data(W25Qxx_TEST_ADDR, wTestBuf, 100);
    for (int i=0;i<100;i++)
    {
        printf("%d ", rTestBuf[i]);
    }
    printf("\r\n");
    read_peripheral_w25qxx_data(W25Qxx_TEST_ADDR, rTestBuf, 100);
    for (int i=0;i<100;i++)
    {
        printf("%d ", rTestBuf[i]);
    }
    printf("\r\n");
}

